#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <stdlib.h>
#include <stdbool.h>
#include "gpiotimer.h"


// warning! need some code to auto initialize array, base on MAXTIMER_NUM
struct GpioTimer {
	struct GPIO* gpio;
	int value;
	int msec;
} gpio_timers[MAXTIMER_NUM] = {
	{ NULL, -1, -1 },
	{ NULL, -1, -1 },
	{ NULL, -1, -1 },
	{ NULL, -1, -1 },
	{ NULL, -1, -1 }
};

struct timeval begin, end;

// now have how many timer exist
bool timer_started = false;
bool has_sig_isr = false;

// stop base timer
void gpiotimer_stop(void)
{
	struct itimerval gpiotime;
	gpiotime.it_interval.tv_sec = 0;
	gpiotime.it_interval.tv_usec = 0;
	gpiotime.it_value = gpiotime.it_interval;
	if(0 != setitimer(ITIMER_REAL, &gpiotime, NULL)) {
		perror("Stop gpio hold iTimer");
	}

}

void gpiotimer_isr(int signum)
{
	int i;
	bool need_stop = true;
	for(i = 0; i < MAXTIMER_NUM; i++) {

		// if the timer used
		if(NULL != gpio_timers[i].gpio) {
			gpio_timers[i].msec--;

			// if the timer timeout
			if(0 == gpio_timers[i].msec) {
				gpio_write(gpio_timers[i].gpio, gpio_timers[i].value);
				gettimeofday(&end, NULL);
				printf("%s use %d usec\n", "read hold time", (end.tv_sec-begin.tv_sec)*1000000 + (end.tv_usec-begin.tv_usec));
				printf("change port %d value to %d\n",
						gpio_timers[i].gpio->portnum, gpio_timers[i].value);

				// destroy this timer
				gpio_timers[i].gpio = NULL;
				gpio_timers[i].value = -1;
				gpio_timers[i].msec = -1;
			} else {
				// if found one timer and its time not zero,
				// then we need timer, and don't stop timer
				need_stop = false;
			}
		}
	}

	if(true == need_stop) {
		printf("stop timer\n");
		gpiotimer_stop();
		timer_started = false;
	}
}

void gpiotimer_start(void)
{
	struct itimerval gpiotime;
	gpiotime.it_interval.tv_sec = 0;
	gpiotime.it_interval.tv_usec = 1000;
	gpiotime.it_value = gpiotime.it_interval;
	if(0 != setitimer(ITIMER_REAL, &gpiotime, NULL)) {
		perror("Start gpio hold timer");
	}

	if(false == has_sig_isr) {
		if(0 != signal(SIGALRM, gpiotimer_isr)) {
			perror("set gpio timer signal");
		} else {
			has_sig_isr = true;
		}
	}
}

int gpiotimer_hold(GPIO* gpio, int value, int msec)
{
	// find a free gpio timer
	int i;
	int free = MAXTIMER_NUM;
	for(i = 0; i < MAXTIMER_NUM; i++) {
		// if this gpio already been added, then overwrite it
		if(gpio == gpio_timers[i].gpio) {
			free = i;
			break;
		}
		if(NULL == gpio_timers[i].gpio) {
			free = i;
		}
	}


	// if no free gpio timer then return
	if(MAXTIMER_NUM == free) {
		return -1;
	}

	// use one timer
	gpio_timers[free].gpio = gpio;
	gpio_timers[free].value = !value;
	gpio_timers[free].msec = msec;

	gpio_write(gpio, value);
	gettimeofday(&begin, NULL);
	if(false == timer_started) {
		printf("start timer\n");
		gpiotimer_start(); // repeat 1ms signal, untill set to 0ms
		timer_started = true;
	}

	return 0;
}
